
// ************************************
// * GStore - Inventory System - thomasfn *
// ************************************
// CLIENTSIDE

module( "gstore", package.seeall )

local Items = {}
local Factories = {}

local BaseItem = {}
local BaseMeta = { __index = BaseItem }

function RegisterFactory( class, tbl )
	Factories[ class ] = tbl
end

function GetFactory( class )
	return Factories[ class ]
end

function LoadFactoryDirect( filename )
	_G.ITEM = {}
	setmetatable( ITEM, BaseMeta )
	ITEM.BaseClass = BaseItem
	include( filename )
	AddCSLuaFile( filename )
	if (ITEM.Base) then
		local base = GetFactory( ITEM.Base )
		if (base) then
			setmetatable( ITEM, { __index = base } )
			ITEM.BaseClass = base
		else
			ErrorNoHalt( "[GSTORE] Unable to derive '" .. ITEM.Classname .. "' from base '" .. ITEM.Base .. "'!\n" )
		end
	end
	RegisterFactory( ITEM.Classname, ITEM )
	Msg( "[GSTORE] Loaded item '" .. ITEM.Classname .. "'!\n" )
	ITEM = nil
end

function LoadFactoryFromTable( ITEM, nobase )
	if (!nobase) then
		setmetatable( ITEM, BaseMeta )
		ITEM.BaseClass = BaseItem
	end
	if (ITEM.Base) then
		local base = GetFactory( ITEM.Base )
		if (base) then
			setmetatable( ITEM, { __index = base } )
			ITEM.BaseClass = base
		else
			ErrorNoHalt( "[GSTORE] Unable to derive '" .. ITEM.Classname .. "' from base '" .. ITEM.Base .. "'!\n" )
		end
	end
	RegisterFactory( ITEM.Classname, ITEM )
	if (ITEM.Base) then
		Msg( "[GSTORE] Loaded item '" .. ITEM.Classname .. "' derived from '" .. ITEM.Base .. "'!\n" )
	else
		Msg( "[GSTORE] Loaded item '" .. ITEM.Classname .. "'!\n" )
	end
end

function LoadFactory( id, nobase )
	_G.ITEM = {}
	if (!nobase) then
		setmetatable( ITEM, BaseMeta )
		ITEM.BaseClass = BaseItem
	end
	include( "factories/" .. id .. ".lua" )
	if (ITEM.Base) then
		local base = GetFactory( ITEM.Base )
		if (base) then
			setmetatable( ITEM, { __index = base } )
			ITEM.BaseClass = base
		else
			ErrorNoHalt( "[GSTORE] Unable to derive '" .. id .. "' from base '" .. ITEM.Base .. "'!\n" )
		end
	end
	RegisterFactory( ITEM.Classname, ITEM )
	if (ITEM.Base) then
		Msg( "[GSTORE] Loaded item '" .. id .. "' derived from '" .. ITEM.Base .. "'!\n" )
	else
		Msg( "[GSTORE] Loaded item '" .. id .. "'!\n" )
	end
	ITEM = nil
end

function LoadBase( filename )
	_G.ITEM = {}
	include( filename )
	table.Merge( BaseItem, ITEM )
	ITEM = nil
	Msg( "[GSTORE] Loaded base item!\n" )
end

function LoadAll( folder )
	for _, v in pairs( file.Find( "../" .. folder .. "/*.lua" ) ) do
		local id = string.Explode( ".", v )[1]
		LoadFactory( id, false )
	end
end

function CreateItem( class, id )
	local item = {}
	local factory = GetFactory( class )
	if (!factory) then return ErrorNoHalt( "Failed to create item '" .. class .. "' (does not exist)!\n" ) end
	setmetatable( item, { __index = factory } )
	item:Init( id )
	Items[ id ] = item
	return item
end

function GetItem( id )
	return Items[ id ]
end

function GetItems()
	return Items
end

function KillItem( item )
	Items[ item._id ] = nil
	item.Invalid = true
end

local function umItemUpdate( um )
	local id = um:ReadShort()
	local class = um:ReadString()
	local inworld = um:ReadBool()
	local pos = um:ReadVector()
	local containerid = um:ReadShort()
	local equipped = um:ReadBool()
	local item = GetItem( id )
	if (!item) then item = CreateItem( class, id ) end
	item.InWorld = inworld
	item:SetPos( pos )
	item.Container = GetItem( containerid )
	if (UpdateInventories && (equipped != item.Equipped) && item.Container) then
		item.Equipped = equipped
		UpdateInventories( item.Container )
	else
		item.Equipped = equipped
	end
	item:Update( um )
	item:Tick()
	print( "[GSTORE] Item data received! [" .. tostring( id ) .. "] (" .. class .. ")" )
end
usermessage.Hook( "gs_item", umItemUpdate )

local function umItemNull( um )
	local id = um:ReadShort()
	local item = Items[ id ]
	if (!item) then return end
	print( "[GSTORE] Item removed by server! [" .. tostring( id ) .. "] (" .. item.Classname .. ")" )
	if (item) then
		item:Destroy()
	end
end
usermessage.Hook( "gs_itemnull", umItemNull )

local nextTick = 0
local function Tick()
	if (CurTime() >= nextTick) then
		nextTick = CurTime() + 0.2
		for _, item in pairs( Items ) do
			item:Tick()
		end
	end
end
hook.Add( "Tick", "gstore.tick", Tick )

local function PlayerBindPress( ply, bind, pressed )
	if (string.find( bind, "use")) && pressed then
		local tr = LocalPlayer():GetEyeTrace()
		local pos = tr.HitPos
		for _, item in pairs( Items ) do
			if (item.InWorld && item:InPickupRange()) then
				if (gstore.HandlePickup) then
					gstore.HandlePickup( item )
				else
					RunConsoleCommand( "gstore_pickup", item:Index() )
				end
				return true
			else
				// print( "Item not in world/range! (" .. item:Index() .. ")" )
			end
		end
	end
end
hook.Add( "PlayerBindPress", "gstore.tick", PlayerBindPress )

local function cmdDumpItems()
	for _, item in pairs( Items ) do
		Msg( tostring( item._id ) .. ": " .. item.Classname .. "\n" )
	end
end
concommand.Add( "gstore_debug_dumpitems", cmdDumpItems )

local function cmdShowInventory()
	local inv = GetItem( LocalPlayer():GetNWInt( "gstore_inv" ) )
	if (!inv) then return ErrorNoHalt( "Failed to load inventory!\n" ) end
	inv:ShowUI()
end
concommand.Add( "gstore_inventory", cmdShowInventory )